package com.demo.utils;

import com.demo.domain.SysUser;
import com.demo.mapper.SysUserMapper;
import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * 重试机制demo
 * @author gxd
 * @since 2025/6/3
 */
public class RetryDemo {
    public static void main(String[] args) {
/*        // 多种策略：
        // 固定间隔等待
        WaitStrategies.fixedWait(1, TimeUnit.SECONDS)
        // 随机等待(1-5秒)
        WaitStrategies.randomWait(1, TimeUnit.SECONDS, 5, TimeUnit.SECONDS)
        // 指数退避(初始100ms，最大5秒)
        WaitStrategies.exponentialWait(100, 5, TimeUnit.SECONDS)
        // 斐波那契退避
        WaitStrategies.fibonacciWait(100, 5, TimeUnit.SECONDS)

        // 停止策略：
        // 尝试N次后停止
        StopStrategies.stopAfterAttempt(5)
        // 超过指定时间后停止
        StopStrategies.stopAfterDelay(10, TimeUnit.SECONDS)
        // 永不停止(慎用)
        StopStrategies.neverStop()
        */
        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                .retryIfExceptionOfType(IOException.class) // 只对IO异常重试
                .retryIfRuntimeException() // 对运行时异常重试
                .retryIfResult(Predicates.equalTo(false)) // 结果为false时重试
                .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 最大重试次数
                .withWaitStrategy(WaitStrategies.fibonacciWait(1, TimeUnit.SECONDS)) // 固定间隔1秒
                .build();

        try {
            // 执行需要重试的操作
            Boolean result = retryer.call(() -> {
                return doSomething(); // 业务方法
            });
            System.out.println("最终结果：" + result);
        } catch (RetryException | ExecutionException e) {
            System.out.println("所有重试尝试都失败了");
            e.printStackTrace();
        }
    }

    private static boolean doSomething() {
        if (Math.random() < 0.7) {
            throw new RuntimeException("操作失败");
        }
        System.out.println("操作成功");
        return true;
    }

    /***
     * HTTP请求重试
     */
    public String httpGetWithRetry(String url) throws Exception {
        Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
                .retryIfExceptionOfType(IOException.class)
                .retryIfResult(String::isEmpty)
                .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.SECONDS))
                .withStopStrategy(StopStrategies.stopAfterAttempt(3))
                .build();

        return retryer.call(() -> {
            try (CloseableHttpClient client = HttpClients.createDefault()) {
                HttpGet request = new HttpGet(url);
                try (CloseableHttpResponse response = client.execute(request)) {
                    return EntityUtils.toString(response.getEntity());
                }
            }
        });
    }

    @Autowired
    private SysUserMapper userMapper;

    /**
     * 数据库操作重试
     */
    public SysUser getUserWithRetry(long userId) throws Exception {
        Retryer<SysUser> retryer = RetryerBuilder.<SysUser>newBuilder()
                .retryIfExceptionOfType(SQLException.class)
                .retryIfResult(Objects::isNull)
                .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.SECONDS))
                .withStopStrategy(StopStrategies.stopAfterDelay(10, TimeUnit.SECONDS))
                .build();

        return retryer.call(() -> userMapper.selectById(userId));
    }
}
